package com.example.util;

import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.web.WebApplicationInitializer;

import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import java.lang.reflect.Modifier;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

/**
 * ServletContainerInitializer接口的实现类通过java SPI声明自己是ServletContainerInitializer的provider.
 * 容器启动阶段依据java spi获取到所有ServletContainerInitializer的实现类，然后执行其onStartup方法.
 * 另外在实现ServletContainerInitializer时还可以通过@HandlesTypes注解定义本实现类希望处理的类型，
 * 容器会将当前应用中所有这一类型（继承或者实现）的类放在ServletContainerInitializer接口的集合参数c中传递进来。
 * 如果不定义处理类型，或者应用中不存在相应的实现类，则集合参数set为空.
 * 这一类实现了 SCI 的接口，如果做为独立的包发布，在打包时，
 * 会在JAR文件的 META-INF/services/javax.servlet.ServletContainerInitializer 文件中进行注册。
 * 容器在启动时，就会扫描所有带有这些注册信息的类(@HandlesTypes(WebApplicationInitializer.class)
 * 这里就是加载WebApplicationInitializer.class类)进行解析，
 * 启动时会调用其 onStartup方法——也就是说servlet容器负责加载这些指定类,
 * 而ServletContainerInitializer的实现者
 * (例如Spring-web中的SpringServletContainerInitializer对接口ServletContainerInitializer的实现中,
 * 是可以直接获取到这些类的)
 * @author kangshuai
 */
public class MySpringServletContainerInitializer implements ServletContainerInitializer {


    @Override
    public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
        List<WebApplicationInitializer> initializers = new LinkedList<>();

        // set 就是servlet3.0规范中为我们收集的 WebApplicationInitializer 接口的实现类的class
        // 从 set 中筛选并实例化出合格的相应的类
        if (set != null) {
            for (Class<?> waiClass : set) {
                if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
                        WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
                    try {
                        initializers.add((WebApplicationInitializer) waiClass.getDeclaredConstructor().newInstance());
                    }
                    catch (Throwable ex) {
                        throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
                    }
                }
            }
        }

        if (initializers.isEmpty()) {
            servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
            return;
        }

        // 这行代码说明我们在实现WebApplicationInitializer可以通过继承Ordered, PriorityOrdered来自定义执行顺序
        AnnotationAwareOrderComparator.sort(initializers);
        servletContext.log("Spring WebApplicationInitializers detected on classpath: " + initializers);

        // 迭代每个initializer实现的方法
        for (WebApplicationInitializer initializer : initializers) {
            initializer.onStartup(servletContext);
        }

    }
}
